/*******************************************************************************
* Copyright (c) 2008, Jeferson Jose de Miranda                                 *
* UDESC - Universidade do Estado de Santa Catarina                             *
* All rights reserved.                                                         *
*                                                                              *
* Redistribution and use in source and binary forms, with or without           *
* modification, are permitted provided that the following conditions are met:  *
*     * Redistributions of source code must retain the above copyright         *
*       notice, this list of conditions and the following disclaimer.          *
*     * Redistributions in binary form must reproduce the above copyright      *
*       notice, this list of conditions and the following disclaimer in the    *
*       documentation and/or other materials provided with the distribution.   *
*     * Neither the name of the Universidade do Estado de Santa Catarina nor   *
*       the names of its contributors may be used to endorse or promote        *
*       products derived from this software without specific prior written     *
*       permission.                                                            *
*                                                                              *
* THIS SOFTWARE IS PROVIDED BY JEFERSON JOSE DE MIRANDA "AS IS" AND ANY        *
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED    *
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE       *
* DISCLAIMED. IN NO EVENT SHALL JEFERSON JOSE DE MIRANDA BE LIABLE FOR ANY     *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  *
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF     *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            *
*******************************************************************************/

/**
 * \file RGBImage.hpp
 *
 * \brief MoveIn::RGBImage extends from MoveIn::Image to create a default
 * three channel color image.
 *
 * \author Jeferson Jose de Miranda
 */

// stdexcept is used for exception throwing.
#include <stdexcept>
// MoveIn:Image is the base class.
#include "Image.hpp"

#ifndef RGBIMAGE_HPP
#define RGBIMAGE_HPP

namespace MoveIn
{
    /**
    * \class RGBImage
    *
    * \brief A three channel color image.
    */
    class RGBImage : public Image
    {
        public:
        /**
        * \fn RGBImage::RGBImage(unsigned short int,
        *                        unsigned short int,
        *                        char*)
        *
        * \brief The image constructor.
        *
        * The constructor of MoveIn::RGBImage takes two parameters: image width,
        * and image height. Depth is 8 bits and number of channels is 3 by
        * default. These last two values can not be changed.
        *
        * \param width
        * The horizontal size of the image.
        * \n Default = 320. This value was used during the tests with this API.
        *
        * \param height
        * The vertical size of the image.
        * \n Default = 240. This value was used during the tests with this API.
        *
        * \param color
        * Hexadecinal color.
        * \n Default = MI_BLACK. Default RGB image will be set in black color.
        */
        RGBImage(unsigned short int width = 320,
                 unsigned short int height = 240,
                 char* color = MI_BLACK);

        /**
        * \fn RGBImage::~RGBImage()
        *
        * \brief The color image destructor.
        */
        ~RGBImage();

        /**
        * \fn void RGBImage::setPixel(unsigned short int,
        *                             unsigned short int,
        *                             RGBColor*)
        *
        * \brief Sets the RGB color values for a pixel in a given coordinate.
        *
        * \param x
        * The x axis (horizontal) pixel index.
        *
        * \param y
        * The y axis (vertical) pixel index.
        *
        * \param color
        * A pointer to a MoveIn class that holds the RGB values.
        */
        void setPixel(unsigned short int x,
                      unsigned short int y,
                      RGBColor *color);

        /**
        * \fn void RGBImage::getPixel(unsigned short int,
        *                             unsigned short int,
        *                             RGBColor*)
        *
        * \brief Retrieves the RGB color calues of a pixel in a given
        * coordinate.
        *
        * \param x
        * The x axis (horizontal) pixel index.
        *
        * \param y
        * The y axis (vertical) pixel index.
        *
        * \param color a reference to a MoveIn class that holds the RGB values.
        */
        void getPixel(unsigned short int x,
                      unsigned short int y,
                      RGBColor *color);

        /**
        * \fn RGBImage::subtract(RGBImage*, RGBImage*)
        *
        * \brief Subtracts front image from back image and stores the result
        * in this RGBImage. The result is a three channel image in
        * pseudo-colors.
        *
        * \param back
        * The RGB reference image.
        *
        * \param front
        * The RGB image witch will be subtracted from reference image.
        */
        void subtract(RGBImage *back, RGBImage *front);

        /**
        * \fn RGBImage::normalize()
        *
        * \brief Converts the RGB values to normalized values in the same
        * color space;
        */
        void normalize();
    };

/*******************************************************************************
* MoveIn::RGBImage::RGBImage                                                   *
*******************************************************************************/

    RGBImage::RGBImage(unsigned short int width,
                       unsigned short int height,
                       char *color)
            : Image(width, height, IPL_DEPTH_8U, 3)
    {
        if(strcmp(color, MI_BLACK))
        {
            RGBColor *c = new RGBColor(color);
            for(int i=0; i<width; i++)
            {
                for(int j=0; j<height; j++)
                {
                    this->setPixel(i, j, c);
                }
            }
        }
    }

/*******************************************************************************
* MoveIn::RGBImage::~RGBImage                                                  *
*******************************************************************************/

    RGBImage::~RGBImage()
    {
    }

/*******************************************************************************
* MoveIn::RGBImage::setPixel                                                   *
*******************************************************************************/

    void RGBImage::setPixel(unsigned short int x,
                            unsigned short int y,
                            RGBColor *color)
    {
        if(x <= getWidth() && y <= getHeight())
        {

            uchar *image_data = (uchar *)image->imageData;

            image_data[y *
                       image->widthStep/sizeof(uchar) +
                       x *
                       image->nChannels] = color->getB();
            image_data[y *
                       image->widthStep/sizeof(uchar) +
                       x *
                       image->nChannels +
                       1] = color->getG();
            image_data[y *
                       image->widthStep/sizeof(uchar) +
                       x *
                       image->nChannels +
                       2] = color->getR();
        }
        else
        {
            throw new OutOfBounds;
        }
    }

/*******************************************************************************
* MoveIn::RGBImage::getPixel                                                   *
*******************************************************************************/

    void RGBImage::getPixel(unsigned short int x,
                            unsigned short int y,
                            RGBColor *color)
    {
        if(x <= getWidth() && y <= getHeight())
        {
            uchar *image_data = (uchar *)image->imageData;

            color->setB(image_data[y *
                                   image->widthStep/sizeof(uchar) +
                                   x *
                                   image->nChannels]);
            color->setG(image_data[y *
                                   image->widthStep/sizeof(uchar) +
                                   x *
                                   image->nChannels +
                                   1]);
            color->setR(image_data[y *
                                   image->widthStep/sizeof(uchar) +
                                   x *
                                   image->nChannels +
                                   2]);
        }
        else
        {
            throw new OutOfBounds();
        }
    }

/*******************************************************************************
* MoveIn::RGBImage::subtract                                                   *
*******************************************************************************/

    void RGBImage::subtract(RGBImage *back,
                            RGBImage *front)
    {
        if(isSameSize(back, front))
        {
            cvAbsDiff(back->getIplImage(),
                      front->getIplImage(),
                      getIplImage());
        }
        else
        {
            throw new DifferentSize();
        }
    }

/*******************************************************************************
* MoveIn::RGBImage::normalize                                                  *
*******************************************************************************/

    void RGBImage::normalize()
    {
        RGBColor color;
        CvRect roi = cvGetImageROI(image);

        for(int x = roi.x; x < roi.width; x++)
        {
            for(int y = roi.y; y < roi.height; y++)
            {
                getPixel(x, y, &color);
                color.normalize();
                setPixel(x, y, &color);
            }
        }
    }
}

#endif

